package ru.ssau.berlin.jsf.beans;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import javax.faces.event.ActionEvent;
import javax.faces.model.SelectItem;
import java.util.logging.Logger;
import javax.ejb.EJB;
import ru.ssau.berlin.ejb.facade.EventFacadeLocal;
import ru.ssau.berlin.ejb.facade.GroupFacadeLocal;
import ru.ssau.berlin.ejb.facade.PersonFacadeLocal;
import ru.ssau.berlin.ejb.facade.UniFacadeLocal;
import ru.ssau.berlin.entity.*;
import ru.ssau.berlin.jsf.util.FacesApplication;

/**
 *
 * @author Caesar
 */

public class EventView {

    private static Logger logger = Logger.getLogger(EventView.class.getName());

    @EJB
    private PersonFacadeLocal personFacade;
    @EJB
    private GroupFacadeLocal groupFacade;
    @EJB
    private EventFacadeLocal eventFacade;
    @EJB
    private UniFacadeLocal uniFacade;


    private static final String ID_PARAMETER = "id_param";

    private FacesApplication application = FacesApplication.getInstance();

    private String value;

    private Event event;
    private List<Event> events;

    private EventType type;
    private List<EventType> types;
    private List<SelectItem> items;

    private EventAttribute attr;
    private List<EventAttribute> attrs;
    private List<SelectItem> attrItems;

    private List<AttributeType> attrTypes;
    private List<SelectItem> attrTypeItems;

    private List<SelectItem> groupItems;
    private List<SelectItem> personItems;
    private PersonGroup group;
    private Person person;

    private List<SelectItem> personRoleItems;
    private AttributeType attrType;
    private String roleValue;

    private boolean readonly;

    /** Creates a new instance of EventView */
    public EventView() {
    }

    public List<Event> getEvents() {
        return events;
    }
    
    public List<EventType> getTypes() {
        return types;
    }

    public Event getEvent() {
        return event;
    }

    public void setEvent(Event event) {
        this.event = event;
    }

    public boolean isReadonly() {
        return readonly;
    }

    public void setReadonly(boolean readonly) {
        this.readonly = readonly;
    }

    public EventType getType() {
        return type;
    }

    public void setType(EventType type) {
        this.type = type;
    }

    public List<SelectItem> getItems() {
        return items;
    }

    public EventAttribute getAttr() {
        return attr;
    }

    public void setAttr(EventAttribute attr) {
        this.attr = attr;
    }

    public List<AttributeType> getAttrTypes() {
        return attrTypes;
    }

    public void setAttrTypes(List<AttributeType> attrTypes) {
        this.attrTypes = attrTypes;
    }

    public List<EventAttribute> getAttrs() {
        return attrs;
    }

    public void setAttrs(List<EventAttribute> attrs) {
        this.attrs = attrs;
    }

    public List<SelectItem> getAttrItems() {
        return attrItems;
    }

    public List<SelectItem> getAttrTypeItems() {
        return attrTypeItems;
    }

    public void setValue(String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }

    public List<SelectItem> getGroupItems() {
        return groupItems;
    }

    public List<SelectItem> getPersonItems() {
        return personItems;
    }

    public PersonGroup getGroup() {
        return group;
    }

    public void setGroup(PersonGroup group) {
        this.group = group;
    }

    public Person getPerson() {
        return person;
    }

    public void setPerson(Person person) {
        this.person = person;
    }

    public List<SelectItem> getPersonRoleItems() {
        return personRoleItems;
    }

    public AttributeType getAttrType() {
        return attrType;
    }

    public void setAttrType(AttributeType attrType) {
        this.attrType = attrType;
    }

    public String getRoleValue() {
        return roleValue;
    }

    public void setRoleValue(String roleValue) {
        this.roleValue = roleValue;
    }

    public String showAllTypes() {
        types = eventFacade.findEventTypes();
        return application.getNavigator().goToEventTypes();
    }

    public String showEventType() {
        loadType();
        types = new ArrayList<EventType>(2);
        if (type.getParent() != null) {
            types.add(type.getParent());
        }
        generateEventTypeSelectItems();
        readonly = true;
        return application.getNavigator().goToEventType();
    }

    public String newEventType() {
        type = new EventType();
        types = eventFacade.findEventTypes();
        generateEventTypeSelectItems();
        readonly = false;
        return application.getNavigator().goToEventType();
    }

    public String persistEventType() {
        if (type.getId() == null) {
            eventFacade.createType(type);
        } else {
            eventFacade.editType(type);
        }
        return showAllTypes();
    }

    public String editEventType() {
        loadType();
        types = eventFacade.findEventTypes();
        removeChildren();
        generateEventTypeSelectItems();
        attrs = eventFacade.findEventAttributes();
        EventType currType = type;
        while (currType != null) {
            attrs.removeAll(currType.getAttributes());
            currType = currType.getParent();
        }
        generateAttributeSelectItems();
        readonly = false;
        return application.getNavigator().goToEventType();
    }

    public void deleteEventType(ActionEvent event) {
        eventFacade.removeType(eventFacade.findEventType(Long.parseLong(
                event.getComponent().getAttributes().get(ID_PARAMETER).toString())));
    }

    public String showAllEvents() {
        events = eventFacade.findAll();
        return application.getNavigator().goToEventsList();
    }

    public String showEvent() {
        logger.log(Level.INFO, "showEvent() entrance");
        event = eventFacade.find(Long.parseLong(
                application.getRequestParameter(ID_PARAMETER)));
        logger.log(Level.INFO, "Event found");
        types = new ArrayList<EventType>(2);
        types.add(event.getType());
        logger.log(Level.INFO, "Types array initialized");
        generateEventTypeSelectItems();
        logger.log(Level.INFO, "Select Items generates");
        generatePersonItems(getFreePersons(event));
        generateGroupItems(getFreeGroups(event));
        generateRoles();
        readonly = true;
        return application.getNavigator().goToEventView();
    }

    public String editEvent() {
        event = eventFacade.find(Long.parseLong(
                application.getRequestParameter(ID_PARAMETER)));
        attrs = new LinkedList<EventAttribute>();
        for (EventType currType = event.getType(); currType != null; currType = currType.getParent()) {
            attrs.addAll(currType.getAttributes());
        }
        for (EventParameter param: event.getParameters()) {
            attrs.remove(param.getAttribute());
        }
        generateAttributeSelectItems();
        types = eventFacade.findEventTypes();
        generateEventTypeSelectItems();
        generatePersonItems(getFreePersons(event));
        generateGroupItems(getFreeGroups(event));
        generateRoles();
        readonly = false;
        return application.getNavigator().goToEventView();
    }

    public String newEvent() {
        event = new Event();
        types = eventFacade.findEventTypes();
        generateEventTypeSelectItems();
        readonly = false;
        return application.getNavigator().goToEventView();
    }

    public String persistEvent() {
        if (event.getId() == null) {
            eventFacade.create(event);
        } else {
            eventFacade.edit(event);
        }
        return showAllEvents();
    }

    public String addParameter() {
        EventParameter param = new EventParameter();
        param.setAttribute(attr);
        param.setEvent(event);
        param.setParameterValue(value);
        eventFacade.createParameter(param);
        event = eventFacade.find(event.getId());
        attrs.remove(attr);
        generateAttributeSelectItems();
        return application.getNavigator().stayHere();
    }

    public void deleteParameter(ActionEvent event) {
        EventParameter param = eventFacade.findParameter(Long.parseLong(
                event.getComponent().getAttributes().get(ID_PARAMETER).toString()));
        eventFacade.removeParameter(param);
        attrs.add(param.getAttribute());
        generateAttributeSelectItems();
        this.event = eventFacade.find(this.event.getId());
    }

    public void deleteEvent(ActionEvent event) {
        this.event = eventFacade.find(Long.parseLong(
                event.getComponent().getAttributes().get(ID_PARAMETER).toString()));
        eventFacade.remove(this.event);
    }

    public void addRole(ActionEvent e) {
        PersonRole role = new PersonRole();
        role.setEvent(event);
        role.setPerson(person);
        role.setRoleValue(roleValue);
        role.setType(attrType);
        uniFacade.createRole(role);
        event.getRoles().add(role);
        generateRoles();
        roleValue = "";
    }

    public String deleteRole() {
        PersonRole role = uniFacade.findRole(Long.parseLong(
                application.getRequestParameter(ID_PARAMETER).toString()));
        event.getRoles().remove(role);
        uniFacade.removeRole(role);
        generateRoles();
        return application.getNavigator().stayHere();
    }

    private void loadType() {
        type = eventFacade.findEventType(Long.parseLong(
                application.getRequestParameter(ID_PARAMETER)));
    }

    private void generateEventTypeSelectItems() {
        items = new ArrayList<SelectItem>(types.size());
        for (EventType iter: types) {
            items.add(new SelectItem(iter, iter.toString()));
        }
    }

    public String showAllAttributes() {
        attrs = eventFacade.findEventAttributes();
        return application.getNavigator().goToEventAttributes();
    }

    public String showAttribute() {
        loadAttribute();
        attrTypes = uniFacade.findAttributeTypes();
        generateAttributeTypeSelectItems();
        readonly = true;
        return application.getNavigator().goToEventAttribute();
    }

    public String newAttribute() {
        attr = new EventAttribute();
        attrTypes = uniFacade.findAttributeTypes();
        generateAttributeTypeSelectItems();
        readonly = false;
        return application.getNavigator().goToEventAttribute();
    }

    public String addAttribute() {
        type.getAttributes().add(attr);
        eventFacade.editType(type);
        attrs = eventFacade.findEventAttributes();
        EventType currType = type;
        while (currType != null) {
            attrs.removeAll(currType.getAttributes());
            currType = currType.getParent();
        }
        generateAttributeSelectItems();
        return application.getNavigator().stayHere();
    }

    public String persistAttribute() {
        if (attr.getId() == null) {
            eventFacade.createAttribute(attr);
            if (type != null) {
                type.getAttributes().add(attr);
                eventFacade.editType(type);
            }
        } else {
            eventFacade.editAttribute(attr);
        }
        return application.getNavigator().goToEventType();
    }

    public String unbindAttribute() {
        if (type != null) {
            loadAttribute();
            type.getAttributes().remove(attr);
            eventFacade.editType(type);
            attrs = eventFacade.findEventAttributes();
            EventType currType = type;
            while (currType != null) {
                attrs.removeAll(currType.getAttributes());
                currType = currType.getParent();
            }
            generateAttributeSelectItems();
        }
        return application.getNavigator().stayHere();
    }

    public String editAttribute() {
        loadAttribute();
        attrTypes = uniFacade.findAttributeTypes();
        generateAttributeTypeSelectItems();
        readonly = false;
        return application.getNavigator().goToEventAttribute();
    }

    public void deleteAttribute(ActionEvent event) {
        attr = eventFacade.findEventAttribute(Long.parseLong(
                event.getComponent().getAttributes().get(ID_PARAMETER).toString()));
        eventFacade.removeAttribute(attr);
        attrs = eventFacade.findEventAttributes();
    }

    public void joinPerson(ActionEvent event) {
        this.event.getPersons().add(person);
        person.getEvents().add(this.event);
        personFacade.edit(person);
        generatePersonItems(getFreePersons(this.event));
    }

    public String leavePerson() {
        person = personFacade.find(Long.parseLong(
                application.getRequestParameter(ID_PARAMETER)));
        event.getPersons().remove(person);
        person.getEvents().remove(event);
        personFacade.edit(person);
        generatePersonItems(getFreePersons(event));
        return application.getNavigator().stayHere();
    }

    public void joinGroup(ActionEvent event) {
        this.event.getGroups().add(group);
        group.getEvents().add(this.event);
        groupFacade.edit(group);
        generateGroupItems(getFreeGroups(this.event));
    }

    public String leaveGroup() {
        group = groupFacade.find(Long.parseLong(
                application.getRequestParameter(ID_PARAMETER)));
        event.getGroups().remove(group);
        group.getEvents().remove(event);
        groupFacade.edit(group);
        generateGroupItems(getFreeGroups(event));
        return application.getNavigator().stayHere();
    }

    private void loadAttribute() {
        attr = eventFacade.findEventAttribute(Long.parseLong(
                application.getRequestParameter(ID_PARAMETER)));
    }

    private void generateAttributeSelectItems() {
        attrItems = new ArrayList<SelectItem>(attrs.size() + 1);
        for (EventAttribute attribute: attrs) {
            attrItems.add(new SelectItem(attribute, attribute.getName()));
        }
    }

    private void generateAttributeTypeSelectItems() {
        attrTypeItems = new ArrayList<SelectItem>(attrTypes.size());
        for (AttributeType iter: attrTypes) {
            attrTypeItems.add(new SelectItem(iter, iter.getName()));
        }
    }

    private void removeChildren() {
        List<EventType> toRemove = new ArrayList<EventType>(types.size() + 1);
        for (EventType iter: types) {
            EventType curr = iter;
            while (curr != null && !curr.equals(type)) {
                curr = curr.getParent();
            }
            if (curr != null) {
                toRemove.add(iter);
            }
        }
        types.removeAll(toRemove);
    }

    private List<Person> getFreePersons(Event event) {
        List<Person> result = personFacade.findAll();
        result.removeAll(event.getPersons());
        return result;
    }

    private List<PersonGroup> getFreeGroups(Event event) {
        List<PersonGroup> result = groupFacade.findAll();
        result.removeAll(event.getGroups());
        return result;
    }

    private void generatePersonItems(List<Person> persons) {
        personItems = new ArrayList<SelectItem>(persons.size());
        for (Person iter: persons) {
            personItems.add(new SelectItem(iter, iter.getName()));
        }
    }

    private void generateGroupItems(List<PersonGroup> groups) {
        groupItems = new ArrayList<SelectItem>(groups.size());
        for (PersonGroup iter: groups) {
            groupItems.add(new SelectItem(iter, iter.getName()));
        }
    }

    private void generateRoles() {
         Set<Person> set = new HashSet<Person>();
         set.addAll(event.getPersons());
         for (PersonGroup iter: event.getGroups()) {
             set.addAll(iter.getPersons());
         }
         for (PersonRole iter: event.getRoles()) {
             set.remove(iter.getPerson());
         }
         personRoleItems = new ArrayList<SelectItem>(set.size());
         for (Person iter: set) {
             personRoleItems.add(new SelectItem(iter, iter.getName()));
         }
    }
    
}
